package com.magnate.software.generator.mybatis.service.impl;

import com.magnate.software.common.utils.PropertyConfigurerUtils;
import com.magnate.software.common.utils.StringUtils;
import com.magnate.software.generator.mybatis.dao.MetaDataDao;
import com.magnate.software.generator.mybatis.domain.bo.DomainBo;
import com.magnate.software.generator.mybatis.domain.enums.MySqlDataType;
import com.magnate.software.generator.mybatis.domain.enums.MySqlKeyType;
import com.magnate.software.generator.mybatis.domain.enums.Settings;
import com.magnate.software.generator.mybatis.domain.po.Column;
import com.magnate.software.generator.mybatis.domain.po.Table;
import com.magnate.software.generator.mybatis.domain.vo.FieldVo;
import com.magnate.software.generator.mybatis.service.GeneratorService;
import com.magnate.software.generator.mybatis.utils.FieldUtils;
import com.magnate.software.generator.mybatis.utils.FreeMarkerUtils;
import com.magnate.software.generator.mybatis.utils.TableUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import javax.annotation.Resource;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 类说明
 *
 * @author WangYang
 * @version 1.0
 * @datetime 2015/12/3 15:16
 */
@Service("generatorService")
public class GeneratorServiceImpl implements GeneratorService {

    private static String author = PropertyConfigurerUtils.getProperty(Settings.AUTHOR.getValue());
    private static String databaseName = PropertyConfigurerUtils.getProperty(Settings.DATABASE_NAME.getValue());

    private static String basePath = PropertyConfigurerUtils.getProperty(Settings.BASE_PATH.getValue());
    private static String domainPath = PropertyConfigurerUtils.getProperty(Settings.DOMAIN_PATH.getValue());
    private static String daoPath = PropertyConfigurerUtils.getProperty(Settings.DAO_PATH.getValue());
    private static String daoImplPath = PropertyConfigurerUtils.getProperty(Settings.DAO_IMPL_PATH.getValue());
    private static String mapperPath = PropertyConfigurerUtils.getProperty(Settings.MAPPER_PATH.getValue());
    private static String mapperXmlPath = PropertyConfigurerUtils.getProperty(Settings.MAPPER_XML_PATH.getValue());

    private static String domainPackageName = PropertyConfigurerUtils.getProperty(Settings.DOMAIN_PACKAGE_NAME.getValue());
    private static String daoPackageName = PropertyConfigurerUtils.getProperty(Settings.DAO_PACKAGE_NAME.getValue());
    private static String daoImplPackageName = PropertyConfigurerUtils.getProperty(Settings.DAO_IMPL_PACKAGE_NAME.getValue());
    private static String mapperPackageName = PropertyConfigurerUtils.getProperty(Settings.MAPPER_PACKAGE_NAME.getValue());

    @Resource
    private MetaDataDao metaDataDao;

    @Override
    public void mybatis() throws Exception {
        List<Table> tables = metaDataDao.getTables(databaseName);
        List<DomainBo> domainBos = new ArrayList<DomainBo>(tables.size());

        String prefix = PropertyConfigurerUtils.getProperty(Settings.TABLE_PREFIX.getValue());
        String suffix = PropertyConfigurerUtils.getProperty(Settings.TABLE_SUFFIX.getValue());

        for (Table item : tables) {
            if (StringUtils.isNotBlank(prefix) && item.getTableName().indexOf(prefix) < 0 || item.getTableName().indexOf("t_front_mall_goods_prorate") < 0) {
                continue;
            }
            List<Column> columns = metaDataDao.getColumns(databaseName, item.getTableName());
            // 构建domain对象信息
            DomainBo domain = initDomain(columns);
            domain.setName(item.getTableName());
            domain.setComment(item.getTableComment());
            domain.setClassName(TableUtils.getClassName(item.getTableName(), StringUtils.isNotBlank(prefix) ? prefix : null, StringUtils.isNotBlank(suffix) ? suffix : null, true));
            domainBos.add(domain);
        }

        for (DomainBo temp : domainBos) {
            temp.setPackageName(domainPackageName);
            temp.setAuthor(author);
            temp.setDatetime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

            buildDomain(temp, "domain.ftl", false);
            if (CollectionUtils.isNotEmpty(temp.getKeys()) && temp.getKeys().size() > 1) {
                buildDomain(temp, "domainKey.ftl", true);
            }
            buildDomainMapperXml(temp,"mapperXml.ftl");

            buildDomainMapper(temp,"mapper.ftl");

            buildDomainDao(temp,"interfaceDao.ftl");

            buildDomainDaoImpl(temp,"interfaceDaoImpl.ftl");
        }
    }

    /**
     * 根据表列字段生成原型构成对象
     *
     * @param columns 列集合
     * @return
     */
    private DomainBo initDomain(List<Column> columns) {
        List<FieldVo> keys = new ArrayList<FieldVo>();
        List<FieldVo> fields = new ArrayList<FieldVo>();
        Set<String> keyImports = new HashSet<String>();
        Set<String> imports = new HashSet<String>();
        for (Column item : columns) {
            FieldVo field = new FieldVo();
            field.setColName(item.getColumnName());
            field.setExtra(item.getExtra());
            field.setName(FieldUtils.getFieldName(item.getColumnName(), "_"));
            field.setComment(item.getColumnComment());
            // 获取列的JAVA属性
            MySqlDataType type = FieldUtils.getFieldTypeByMysql(item.getDataType());
            if (type != null) {
                String packageName = null;
                String javaType;
                // 判断字段是否是无符号类型
                if (item.getColumnType().contains("unsigned")) {
                    if (StringUtils.isNotBlank(type.getUnsignedPackageName())) {
                        packageName = type.getUnsignedPackageName();
                    }
                    javaType = type.getUnsignedType();
                } else {
                    if (StringUtils.isNotBlank(type.getPackageName())) {
                        packageName = type.getPackageName();
                    }
                    javaType = type.getJavaType();
                }
                field.setColType(type.getJdbcType());
                // 设置JAVA类型
                field.setType(javaType);
                // 判断是否是主键
                if (item.getColumnKey().toUpperCase().equals(MySqlKeyType.PRI.getValue())) {
                    if (StringUtils.isNotBlank(packageName)) {
                        keyImports.add(packageName);
                    }
                    keys.add(field);
                } else {
                    if (StringUtils.isNotBlank(packageName)) {
                        imports.add(packageName);
                    }
                    fields.add(field);
                }
            }
        }
        DomainBo domain = new DomainBo();
        // 设置主键
        domain.setKeys(CollectionUtils.isEmpty(keys) ? null : keys);
        // 设置主键引用
        domain.setKeyImports(CollectionUtils.isEmpty(keyImports) ? null : keyImports);
        // 设置列字段
        domain.setColumns(CollectionUtils.isEmpty(fields) ? null : fields);
        // 设置引用
        if (CollectionUtils.isEmpty(keys) && keys.size() == 1 &&  CollectionUtils.isEmpty(keyImports)) {
            imports.addAll(keyImports);
        }
        domain.setImports(CollectionUtils.isEmpty(imports) ? null : imports);
        return domain;
    }

    private void buildDomain(DomainBo domain, String templateName, Boolean isKey) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = basePath + "/" + domainPath;

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + (isKey ? domain.getClassName() + "Key" : domain.getClassName()) + ".java");

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void buildDomainMapperXml(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = basePath + "/" + mapperXmlPath;

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + ".xml");

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", mapperPackageName);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void buildDomainMapper(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = basePath + "/" + mapperPath;

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "Mapper.java");

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", mapperPackageName);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void buildDomainDao(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = basePath + "/" + daoPath;

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "Dao.java");

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("daoPackageName", daoPackageName);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void buildDomainDaoImpl(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = basePath + "/" + daoImplPath;

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "DaoImpl.java");

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", mapperPackageName);
            params.put("daoPackageName", daoPackageName);
            params.put("daoImplPackageName", daoImplPackageName);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private File getTemplateDir() throws Exception {
        return ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "ftl");
    }

}
